/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package racer.client.gui.mapgen;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import javax.swing.JOptionPane;
import racer.client.data.RegEx;



/**
 *
 * @author Lieven
 */


//called static methods (below in this file) by mapgenerator.java
//through instance by game.java
public class TiledGrid {



    //x_current_tile_building_map en y_current_tile_building_map zijn coordinaten van vorige painted image
    private int x_current_tile_building_map=0;
    private int y_current_tile_building_map=0;
        //tile_id_counter om ids toe te kennen aan elke tile bij het opbouwen van de map
    //later ->replace by getwidth en getHeight of bufferedImage (always previous one)
    //especially needed when building map...atm only 1 tile size so np
    static final int img_tile_width=100;
    static final int img_tile_height=100;








 /**
 *This method is used to load a project map,once saved by MapGenerator.Loading means building (putting the tiles in "chosenmap"-HashMap) and painting it.
 *<br>Such a map contains tile ids and tile type (image to paint on a certain tile)
  * <br>This method also analyses the loaded map to set some booleans in the main application
  * <br>f.e. is there allready finish line?,..
 * @param mapg MapGenerator-object for using setters (set chosenmap,set finishline_present boolean)
 * @param filename file to load
 * @return boolean loading succeeded or failed
 * @see MapGenerator#getChosenMap()

 */

   public static boolean loadMapInProgress(MapGenerator mapg,File mapInProgressFile){
       String expressie="paintMapInProgressItem=(.*?),(.*?)=";
       HashMap chosenmap = new HashMap();

       //needs to stay local
       boolean finishtest=false;
       //File mapInProgressFile = new File(filename);
    try {
      //use buffering, reading one line at a time
      //FileReader always assumes default encoding is OK!
      BufferedReader input =  new BufferedReader(new FileReader(mapInProgressFile));
      try {
        String line = null; //not declared within while loop
        /*
        * readLine is a bit quirky :
        * it returns the content of a line MINUS the newline.
        * it returns null only for the END of the stream.
        * it returns an empty String if two newlines appear in a row.
        */
        while (( line = input.readLine()) != null){


            Matcher vergelijk = RegEx.getMatcher(expressie,line);

            if (RegEx.Matches(vergelijk,0)){



                    try{

                    //paintMapItem=V,V,boven,3,g=
                   //     (string apart nodig)
                   int tile_id = Integer.parseInt(vergelijk.group(1));
                   String parsed_prentje_string = vergelijk.group(2).toString();

                   //true zetten opdat de boolean ook ok is als men de map opslaat na het openen van een vorig project
                   //normaal checkt hij het type tile bij het tekenen/toevoegen en/of verwijderen
                   if((parsed_prentje_string.equals("HFIN"))||((parsed_prentje_string.equals("VFIN")))){
                        finishtest=true;
                   }



                   //this.ouder.getGame().setTegenstanderPosition(x, y, angle,active_tile_tegenstander);

                   System.out.println(parsed_prentje_string+","+tile_id);
                   //hashmap opvullen
                   chosenmap.put(tile_id,parsed_prentje_string);

                    }catch(NumberFormatException nu){
                        System.err.println("fout"+nu);

                    }




            }
        }

      }
      finally {
        input.close();
        System.out.println("done");
        //System.exit(0);
        if(chosenmap.size() == 0){
            return false;
        }else{
           if(finishtest){
                mapg.setfinish_line_present(true);
           }else{
                mapg.setfinish_line_present(false);
           }
           mapg.setChosenMap(chosenmap);
           return true;
        }

      }
    }
    catch (IOException ex){
      ex.printStackTrace();
      return false;
    }

  }

 /**
 *This method is used to save a project map,created by MapGenerator.Saving means (after being built/put in tile hashmap aka chosenmap) iterating HashMap and printing it to file.<br>You can load this in MapGenerator to modify it (not used as gaming map,see exportmap).
 *<br>Such a map contains tile ids and tile type (image to paint on a certain tile)
 * @param mapg MapGenerator-object for using getter (get chosenmap)
 * @param filename file to save
 * @return boolean saving succeeded or failed
 * @see MapGenerator#getChosenMap()

 */


   public static boolean saveMapInProgress(MapGenerator mapg,File mapInProgressFile){
     try{


        FileWriter fstream = new FileWriter(mapInProgressFile);
        BufferedWriter out = new BufferedWriter(fstream);
        HashMap chosenmap=mapg.getChosenMap();

         Iterator cmIterator = chosenmap.keySet().iterator();
        while(cmIterator.hasNext()) {
            int mid = (Integer)cmIterator.next();
            String type = (String)chosenmap.get(mid);
            System.out.println("saving mapInProgress tile ID:"+mid+", type"+type+"=");
            out.write("paintMapInProgressItem="+mid+","+type+"=");
            out.newLine();

        }

    //Close the output stream
    out.close();
    return true;
    }catch (Exception e){//Catch exception if any
      System.err.println("Error: " + e.getMessage());
      return false;
    }


  }




   public static boolean exportMap(MapGenerator mapg,int amountOfTiles,int cols){

       //later nog opkuisen
      HashMap chosenmap=mapg.getChosenMap();
      HashMap tile_map=mapg.getTileMap();
      HashMap typetilemap=mapg.getTypetilemap();
      HashMap typetileStringMap=mapg.gettypetileStringMap();
      HashMap tileIDdistanceBetweenDirectionsMap = mapg.gettileIDdistanceBetweenDirectionsMap();
      //HashMap checkAllElementsmap=mapg.getChosenMap();
      ArrayList checkAllElementsmap = new ArrayList();
      //resetting when earlier tried to export
      mapg.setallElementsUsedInChainedMap(true);
      //analyseMapBeforeSaving
        int mid=1;
        //willekeurig id bepalen die in de chosenmap zit en vandaar vertrekt het script
         Iterator cm2Iterator = chosenmap.keySet().iterator();
        if(cm2Iterator.hasNext()) {
            mid = (Integer)cm2Iterator.next();
        }

            //begincoordinaat van map meegeven
            Tile tiler = (Tile)tile_map.get(mid);
            int startmapMINX = tiler.x;
            int startmapMINY=tiler.y;

        String FileString="";

        String type = (String)chosenmap.get(mid);
        //eerste element onder zette
       FileString+="paintMapItem="+type+",onder,1=\n";
        System.out.println("Tile Type:"+type+", richting:onder ,aantal keer:"+1);
        //bij eerste element/tile zal hij eerste boolean op zen pad nemen
        boolean left=true;
        boolean right=true;
        boolean top=true;
        boolean bottom=true;
        //startpositie...als het toevallig niet zo gevonden worden later..is het de positie van de eerste tile
        //LATER: y in 100 want in game.jave wordt de eerste tile naar onder gezet = +100 in de y richting
        int finishline_y=tiler.y;
        int finishline_x=tiler.x;

        int tileteller=1;
        //richting
        String previous="";
        String prevprev="rechts";
        //String thPrevious="";
        //voor de eerste keer
        String previous_type="onder";
        //String next_tile_placement="rechts";
        int prev_type_teller = 1;
        //<= voor de controle of de laatste gelinkt is aan de eerste
        //maar op einde laatste regel niet meer uitprinten naar de file(bestaat al)
        //print.chosentiles(chosenmap);
        while(tileteller <= (chosenmap.size()+1)){
            tileteller++;

            //print.out(""+mid);


            //script hieronder zal vanaf een tile beginnen zoeken naar zijn gevulde buren en zo een chain maken
            //wanneer er een volgende mid niet bestaat..dwz dat er een gat is in de chain
            //maar er moet ook controle komen over het aantal gebruikte elementen in de chain
            //want je kan een chained tilevorm maken maar als er nog elders een vakje ingevuld staat....
            //vandaar de checkAllElementsmap

            if (!chosenmap.containsKey(mid)){

             mapg.setisMapCircular(false);
             System.out.println("map niet circulair");
             return false;
            }else{

                //allready used
                if(!checkAllElementsmap.contains(mid)){
                    checkAllElementsmap.add(mid);
                }
                }


            //zowieso al 1 finishline vooraleer men deze methode oproept (controle wanneer men wil saven
            //enkel nog coördinaat zoeken
            if((type.equals("HFIN"))||(type.equals("VFIN"))){
                finishline_x = ((Tile)tile_map.get(mid)).x;
                finishline_y= ((Tile)tile_map.get(mid)).y;
            }

            //toch nog nodig voor circular check
            switch (Integer.parseInt(typetilemap.get(type).toString())){

            //steeds een x en y waarde vergelijken van de tile met positie van de auto (overschreden? => collision)
            //als de tegenovergestelde x en y waarde overschreden is : nieuwe tile

             //x en y elk een if/else geven (nooit buurrichtingen in 1 2delige-if/else structuur)
            //geen onlogische waarschuwing aangezien men 2 richtingen per collision hebt en 2 richtingen om actieve tile te triggeren
            case 1:
                //H

                if(left){

                    right=false;

                    top=true;
                    bottom=true;

                    previous="links";

                }else if(right){
                    previous="rechts";
                    top=true;
                    bottom=true;
                    left=false;
                }


            break;

            case 2:
                //V

                if(bottom){
                    previous="onder";
                    top=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                }

            break;

            case 3:
                //K1

                if(bottom){
                    previous="onder";
                    top=false;
                    right=true;
                    left=true;
                }else if(left){
                    previous="links";
                    top=true;
                    bottom=true;
                    right=false;
                }

            break;

            case 4:
                //K2

                if(right){
                    top=true;
                    bottom=true;
                    previous="rechts";
                    left=false;
                }else if(bottom){
                    previous="onder";
                    top=false;
                    right=true;
                    left=true;
                }


            //bepaal de 2 rijrichtingen
            //rechts of onder


            break;

            case 5:
                //K3

                if(right){
                    top=true;
                    bottom=true;
                    previous="rechts";
                    left=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                    right=true;
                    left=true;
                }
            break;

            case 6:
                //K4
                if(left){
                    top=true;
                    bottom=true;
                    previous="links";
                    right=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                    right=true;
                    left=true;
                }
            break;

                default:
                    break;
            }
            mid+=(Integer)tileIDdistanceBetweenDirectionsMap.get(previous);
            previous_type=type;
            type = (String)chosenmap.get(mid);
            if(tileteller <= chosenmap.size()){
            FileString+="paintMapItem="+type+","+previous+",1=\n";
            System.out.println("Tile Type:"+type+", richting: "+previous+" ,aantal keer:"+prev_type_teller);
                }








        }

        if(checkAllElementsmap.size() != chosenmap.size()){
             mapg.setallElementsUsedInChainedMap(false);
             System.out.println("not all ids are used ");
             return false;
        }

        //test of elk element voorkomt in de chain


        //vanaf hier kan men de map vullen

        //max x zoeken
        //max y zoeken
        //dimensie bepalen (aantal spaties)
        //voorlopig dimensie bepalen via hoogst ingevulde id

        int tile_max_x=0;
        int tile_max_y=0;
        int tile_max_id_x = 0;
        int tile_max_id_y = 0;

         Iterator tileIterator = chosenmap.keySet().iterator();
        while(tileIterator.hasNext()) {

            Tile tee = (Tile)tile_map.get(tileIterator.next());

            //latest furthest x max
            if(tee.x >= tile_max_x){
                tile_max_x=tee.x;
                tile_max_id_x=tee.id;
            }
            //latest furthest y max
            if(tee.y >= tile_max_y){
                tile_max_y=tee.y;
                tile_max_id_y=tee.id;
            }

        }
         int curcols=tile_max_x/100;
         int currows=tile_max_y/100;
        /*int tile_max_x=0;
        int tile_max_y=0;

         Iterator tileIterator = chosenmap.keySet().iterator();
        while(tileIterator.hasNext()) {

            Tile tee = (Tile)tile_map.get(tileIterator.next());
            if(tee.x > tile_max_x){
                tile_max_x=tee.x;
            }
            if(tee.y > tile_max_y){
                tile_max_y=tee.y;
            }
        }    */

        //tile_max zijn bepaald => dimensie dat men nodig heeft om weg te printen naar file

        FileString="";
        int tilecounter=0;
        int col_count=0;
        boolean newrow=false;
        //mogelijk omdat hij van links naa rechts nummert in de kolommen...rij per rij
        for(int tileid=1;tileid<=amountOfTiles;tileid+=1){
            
            col_count++;
            if (tileid % cols == 0){
                col_count=0;
                newrow=true;
            }else{
                newrow=false;
            }
            
            
            Tile tee = (Tile)tile_map.get(tileid);
            
            //row by row,column by column
            if ((tee.y <= tile_max_y)){//&&(tee.x <= tile_max_x)){
                //tilecounter++;

                if (chosenmap.containsKey(tileid)){
                    //type opvragen
                    FileString+=typetileStringMap.get((chosenmap.get(tileid))).toString();

                }else{
                    //Tile tee2 = (Tile)tile_map.get(tileid);
                   // if (tee2.y <= tile_max_y && tee2.x <= tile_max_x)
                     // if(tileid % cols <= tile_max_id_x % cols)
                        FileString+=" ";
                }
                if(newrow){
                    FileString+="\n";
                }
            }

        }
      //  while (tilecounter % cols != 0){
      //      FileString+=" ";
        //    tilecounter++;
        //}

         //
            int start_mapX= startmapMINX;
            int start_mapY= startmapMINY;

             try{
            // Create file
            String filename="";
            while (filename.equals("")){
		filename = JOptionPane.showInputDialog(null, "Enter a name for the map :" , 1);
            }



            FileWriter fstream = new FileWriter("maps/"+filename);
            BufferedWriter out = new BufferedWriter(fstream);

            //out.write("Setting-startmap="+start_mapX+","+(start_mapY-100)+"=\n");
            //out.write("Setting-startpositie="+finishline_x+","+finishline_y+"=\n");
            out.write(FileString);
            //Close the output stream
            out.close();
            return true;
            }catch (Exception e){//Catch exception if any
              System.err.println("Error: " + e.getMessage());
              return false;
            }






  }
 /**
 *This method is used to create/build an empty grid/rastered map. A HashMap is being filled with all tiles(including its coordinates in the field,calculated by rows/cols/tile_height/tile_width)<br>possible in the dimension formed by the amount of columns/rows of the map.
 *<br>The tile boundaries are painted in black(combined with drawRasteredMap) and the HashMap is passed to MapGenerator object.
 * @param mapg MapGenerator-object for using setters (setting chosenmap null and setting tile_map that just got filled)
 * @param cols amount of columns to draw
 * @param rows amount of rows to draw
 * @param tile_width
 * @param tile_height
 *@see drawRasteredMap(Graphics g,int cols,int rows,int tile_width,int tile_height)
 * @see MapGenerator#setChosenMap(HashMap)
 * @see MapGenerator#setTileMap(HashMap)
 * @see MapGenerator

 */

  public static void buildRasteredMap(MapGenerator mapg,int cols,int rows,int tile_width,int tile_height){

       HashMap tile_map = new HashMap();
       int tileid=0;
          for(int j =0;j<(rows*tile_height);j+=tile_height){
            for(int i =0;i<(cols*tile_width);i+=tile_width){

                 tileid++;
                 //TileTypeNeighbours ttnteep=new TileTypeNeighbours(tileid+"",0,0,0,0);
                 Tile newtile = new Tile(tileid,i,j,100,100,"empty");//,ttnteep);
                 tile_map.put(tileid, newtile);

            }
          }
       mapg.setTileMap(tile_map);
       //this will be filled when peope fill a tile
       mapg.setChosenMap(new HashMap());
  }



  //PAINT METHODS
 /**
 *This method will paint tile-id of the active tile (where the entity(car) is driving)
 *(only in Debug-mode)
 *
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  tileidstr the text to print/paint
 */
  public static void paintActiveTileNumber(Graphics g,int x,int y,int w,int h,String tileidstr){
            g.setColor(Color.BLACK);
            g.drawString(tileidstr, (x+(w/8)), (y+(h/5)));
  }

 /**
 *This method will paint a rectangle at the left top corner(always) of the car image (where the entity(car) is driving)
 *(only in Debug-mode). It uses paintCollisionRectangle()-method.
 *
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  c color
  * @see paintCollisionRectangle(Graphics g,int x,int y,int w,int h,Color c)
 */

 public static void paintImagePositionCoordinate(Graphics g,int x,int y,int w,int h,Color c){
      paintCollisionRectangle(g,x,y,w,h,c);
 }
/**
 *This method will paint a rectangle at the given position. Here its the collisionpoint of the car image (depending on driving direction and angle of the car)
 *(only in Debug-mode). It uses fillrect-method.
 *
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  c color
 */

 public static void paintCollisionRectangle(Graphics g,int x,int y,int w,int h,Color c){
            g.setColor(c);
            g.fillRect(x,y,w+5,h+5);
 }
 /**
 *This method is used to paint an empty grid/rastered map.(combined with buildRasteredMap)
 *<br>The tile boundaries are painted in black.
 * @param g Graphics object (output to screen)
 * @param cols amount of columns to draw
 * @param rows amount of rows to draw
 * @param tile_width
 * @param tile_height
 * @see buildRasteredMap(MapGenerator mapg,int cols,int rows,int tile_width,int tile_height)
 * @see MapGenerator

 */

  public static void drawRasteredMap(Graphics g,int cols,int rows,int tile_width,int tile_height){

          g.setColor(Color.BLACK);
          for(int j =0;j<(rows*tile_height);j+=tile_height){
            for(int i =0;i<(cols*tile_width);i+=tile_width){
                g.drawRect(i,j,100,100);
                }
            }
    }
/**
 *This method will paint rectangle boundary lines at the given position of the active_tile.
 *(in Debug-mode if used in game application,normal mode if used in MapGenerator). It uses drawrect-method.
 *
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 */

  public static void paintActiveTileBounds(Graphics g,int x,int y,int w,int h){
            g.setColor(Color.RED);
            g.drawRect(x,y,w,h);
  }


  /**
 *This method will paint a chosen tile type (picture) at the given position of the active_tile.
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param image2draw BufferedImage to paint (tile_type)
 */


  public static void paintFillActiveTile(Graphics g,int x,int y,int w,int h,BufferedImage image2draw){
        g.drawImage(image2draw, x, y, null);
  }

  /**
 *This method will paint a filled red rectangle (filled with WRONG TILE text) at the given position of the active_tile. It means that <br>when the map creator/user puts down a chosen tile,an error occured (not allowed tile type on that position)
 * @param  g Graphics object (output to screen)
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 */

  public static void paintTileError(Graphics g,int x,int y,int w,int h){

            g.setColor(Color.RED);
            g.fillRect(x, y, w, h);
            g.setColor(Color.BLACK);
            g.drawString("WRONG TILE",(x+5),(y+(h/2)));
    }

   /**
 *This method fills the tyle by paiting the picture of the tile type
 *<br>It uses 2 coordinates (start position to draw) defined in add_filled_Tile_To_Map()-method :x_current_tile_building_map,y_current_tile_building_map
 * @param  g Graphics object (output to screen)
 * @param  soortpic BufferedImage,for each tile type there is a different image to paint
 * @see    add_Filled_Tile_To_Map(BufferedImage bochtnummer,String type,String richting,int times,Graphics g)
 */

    public void paintFilledTile(BufferedImage soortpic,Graphics g){
        g.drawImage(soortpic, x_current_tile_building_map, y_current_tile_building_map, null);

    }


}
